package comp512.classes;

import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.BindException;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;

import comp512.gui.ServerGUI_Main;

public class Server implements Runnable {

	List<Player> unactivePlayers;
	Map<String, List<Player>> activeSessionPlayers;
	List<Player> activePlayers;
	private static final int PORT = 6060;
	private ServerSocket serverSocket;
	private Socket clientSocket;
	public final AtomicBoolean serverRun;
	private String serverAddress;
	public final AtomicInteger serverPort;
	private ServerGUI_Main guiInstance;
	private static Server instance = null;

	public Server() {
		if(instance == null)
			instance = this;
		guiInstance = ServerGUI_Main.getInstance();
		unactivePlayers = polulateExistingPlayers();
		activePlayers = new ArrayList<>();
		activeSessionPlayers = new HashMap<String, List<Player>>();
		serverPort = new AtomicInteger(-1);
		serverRun = new AtomicBoolean(true);
		try {
			serverAddress = InetAddress.getLocalHost().getHostAddress();
		} catch (UnknownHostException e) {
			e.printStackTrace();
		}
		new Thread(this).start();
	}

	public void init(){
		guiInstance.init();
		guiInstance.setVisible(true);		
	}
	
	public Question getQuestion(){
		Question q = null;
		DB db = DB.getInstance();
		q = db.getRandomQuestion();		
		return q;
	}
	
	public static Server getInstance() {
		if (instance == null)
			instance = new Server();
		return instance;
	}


	private List<Player> polulateExistingPlayers() {
		//TODO Auto-generated method stub
		return new ArrayList<>();
	}
	
	private void saveData(){
		
	}

	public void clearPlayers() {
		if (activePlayers != null)
			activePlayers.clear();
		if (unactivePlayers != null)
			unactivePlayers.clear();
	}
	
	public Player getSavedPlayer(Player player) {
		synchronized (unactivePlayers) {
			for(Player savedplayer: unactivePlayers){
				if(savedplayer.equals(player)){
					return savedplayer;
				}
			}
		}
		return null;
	}

	public void addPlayerToLobby(Player player) {
		System.out.println("Adding " + player.getPlayerId());
		List<Player> players = new ArrayList<>();
		synchronized (activePlayers) {
			if (activeSessionPlayers.containsKey("LOBBY")) {
				players = activeSessionPlayers.get("LOBBY");
			}
			players.add(player);
			activeSessionPlayers.put("LOBBY", players);
		}
		System.out.println("Clients: " + players.size());
		if (guiInstance.setCounter(String.valueOf(players.size())))
			System.out.println("GUI Updated");
		else
			System.out.println("Update Failed.");

	}

	@SuppressWarnings("unused")
	private void log(String message) {
		// TODO
	}

	public void startServer() {
		this.serverRun.set(true);
	}

	public void stopServer() throws IOException {
		this.serverRun.set(false);
		serverSocket.close();
		saveData();
	}

	@Override
	public void run() {
		try {
			serverSocket = new ServerSocket(PORT);
			System.out.println("Server Started");

			serverPort.set(serverSocket.getLocalPort());

			while (serverRun.get()) {
				clientSocket = serverSocket.accept();
				System.out.println("Accepting new client... "
						+ clientSocket.getRemoteSocketAddress());
				try {
					ObjectInputStream ois = new ObjectInputStream(
							clientSocket.getInputStream());
					Message message = (Message) ois.readObject();
					if (message != null) {
						System.out.println("Message: " + message.getFlag());
						//handleMessage(message, clientSocket);
						new ServerHandlerThread(message, clientSocket).start();
					} else
					{
						System.out.println("No Message.");
					}
				} catch (SocketException se) {
					System.out.println("Got a Socket Exception");
					System.err.println("Socket Exception: " + se.getMessage());
					se.printStackTrace();
				} catch (Exception e) {
					System.err.println(e.getMessage());
					e.printStackTrace();
				}
			}
			System.out.println("Closed t1....");
		} catch (BindException e) {
			System.err.println("Bind Exception: " + e.getMessage());
			e.printStackTrace();
		} catch (IOException ix) {
			System.err.println("IO Exception: " + ix.getMessage());
			ix.printStackTrace();
		} finally {
			try {
				serverSocket.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}

	}

	public String getAddress() {
		return serverAddress;
	}

	public String getPort() {
		return serverPort.toString();
	}

	public Map<String, List<Player>> getActiveSessionPlayers(){
		return activeSessionPlayers;
	}
	
	public void setActiveSessionPlayers(Map<String, List<Player>> activeSessionPlayers){
		this.activeSessionPlayers = activeSessionPlayers;
	}
}
